﻿using NUnit.Framework;
using System;
using LucyGrid;
using System.Threading.Tasks;
using System.Linq;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Reflection;

namespace LucyGridNUnitTest
{
	[TestFixture ()]
	public class Test
	{
		[Test ()]
		public void TestCase ()
		{
		}

		GridComm gcomm;

		[TestFixtureSetUp] 
		public void Initialize()
		{
			gcomm = new GridComm().StartListener((m) =>
				{
					GridUtils.NotifyLog("TEST,INFO",m);
				});

		}


		[Test ()]
		public void  Grid_TestGrid()
		{
			var l = new GridComm().StartListener();

			int test = 50;
			// Run a worker
			var t = StartGridNode(1);
			var res = GridMe(test);
			// Close nodes
			new GridComm().EmitStopAll();
			Assert.IsTrue(res == test, "Error on the resulting number");
			Assert.IsTrue(Task.WaitAll(t, 5000), "Error waiting tasks to finish");
		}

		[Test ()]
		public void Grid_TestRemoteNode_remote_must_be_running()
		{
			// var l = new GridComm().StartListener();

			int test = 50;
			// Run a worker
			//var t = StartGridNode(1);
			var res = GridMe(test);
			// Close nodes
			//new GridComm().EmitStopAll();
			Assert.IsTrue(res == test, "Error on the resulting number");
			// Assert.IsTrue(Task.WaitAll(t, 5000), "Error waiting tasks to finish");
		}

		[Test ()]
		public void Grid_TestMultiGrid()
		{
			// Run a worker
			var t = StartGridNode(3);
			Enumerable
				.Range(0, 4)
				.AsParallel()
				.Select(i => GridMe(20))
				.ToArray();
			// Close nodes
			new GridComm().EmitStopAll();
			Assert.IsTrue(Task.WaitAll(t, 1000));
		}

		Task[] StartGridNode(int nodes)
		{
			return Enumerable.Range(0, nodes)
				.Select(i =>
					Task.Run(() =>
						{
							var dir = Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"..\..\..\..\LucyGridNode\bin\debug\");
							var file = Directory.GetFiles(dir).First(f => f.EndsWith(".exe"));
							var p = Process.Start(file);
							while (!p.WaitForExit(500))
								p.Refresh();
							GridUtils.NotifyLog("TEST,INFO","Process finished");
						})).ToArray();
		}



		private int GridMe(int test)
		{
			return Enumerable.Range(0, test)
				.Select(s => new
					{
						Length = 3,
						IsNull = false,
						Complex = new
						{
							CompexLevel = 1,
							Complex = new
							{
								CompexLevel = 2,
								ExternalRef = new SerializableClass()
								{
									Data = "aaarghh",
									IntData = 4,
									SomeArrayData = new string[] { "a", "b" }
								}
							}
						}
					})
				.AsParallelGrid(d => new
					{
						Lenght2 = d.Length * 2,
						Complex = new
						{
							CompexLevel = 1,
							Complex = new
							{
								CompexLevel = 2,
								d.Complex.Complex.ExternalRef,
								ExternalRef2 = new SerializableClass()
								{
									Data = "From GRID",
									IntData = 100000,
									SomeArrayData = new string[] { "G", "R", "I", "D" }
								}
							}
						}
					})
				.Count();
		}

		//[Test ()]
		public void TestNestedGrid()
		{
			Enumerable
				.Range(0, 100)
				.Select(i => new { Value = i })
				.AsParallelGrid(i =>
					{
						var val = Enumerable
							.Range(0, 100)
							.Select(c => new { Value = c })
							.AsParallelGrid(ii =>
								{
									return new { Total = ii.Value + 1 };
								})
							.Sum(cc => cc.Total);
						return new
						{
							Total = val
						};
					})
				.ToArray();
		}

		[Serializable]
		public class SerializableClass
		{
			public string Data;
			public int IntData;
			public string[] SomeArrayData { get; set; }
		}

		[Serializable]
		public class Complex
		{
			public int CompexLevel = 1;
			public SerializableClass ExternalRef;
			public Complex ComplexObject;
		}

		[Serializable]
		public class SerializationTestClass {
			public int Length = 3;
			public bool IsNull = false;

			public Complex Complex = new Complex();
			public SerializableClass[] ArrayOfObject = new SerializableClass[] { 
				new SerializableClass(){
					IntData = 3
				}
			};
		}

		[Test ()]
		public void Utils_TestSerialization()
		{
			var testObject = new SerializationTestClass()
			{
				Length = 3,
				IsNull = false,
				Complex = new Complex()
				{
					CompexLevel = 1,
					ComplexObject = new Complex()
					{
						CompexLevel = 2,
						ExternalRef = new SerializableClass()
						{
							Data = "aaarghh",
							IntData = 4,
							SomeArrayData = new string[] { "a", "b" }
						}
					}
				},
				//ListOfObjects = new List<NoMarketAsSerializableClass>(),
				ArrayOfObject = new SerializableClass[] { 
					new SerializableClass(){
						IntData = 3
					}
				}
			};
			var res = SerializeCustom(testObject);
			GridUtils.NotifyLog("TEST,INFO","Custom serialization takes: " + res[0] + " for " + res[1] + " bytes");
			res = SerializeBinary(testObject);
			GridUtils.NotifyLog("TEST,INFO","Binary serialization takes: " + res[0] + " for " + res[1] + " bytes");

		}

		public long[] SerializeCustom(object testObject) {
			var sp = System.Diagnostics.Stopwatch.StartNew();
			sp.Start();
			using (var ms = new MemoryStream())
			{
				GridUtils.SerializeCustom(ms, testObject);
				ms.Seek(0, SeekOrigin.Begin);
				var size = ms.Length;
				var o = GridUtils.DeserializeCustom(ms, testObject.GetType());
				sp.Stop();
				var time = sp.ElapsedMilliseconds;

				var jsonconv = new Newtonsoft.Json.JsonSerializer();
				var writer = new StringWriter();
				jsonconv.Serialize(writer, testObject);
				var json1 = writer.ToString();
				writer = new StringWriter();
				jsonconv.Serialize(writer, o);
				var json2 = writer.ToString();
				Assert.IsTrue(json1.Equals(json2));
				return new long[] { time, size };
			}
		}

		public long[] SerializeBinary(object testObject)
		{
			var sp = System.Diagnostics.Stopwatch.StartNew();
			sp.Start();
			using (var ms = new MemoryStream())
			{
				GridUtils.SerializeBinary(ms, testObject);
				ms.Seek(0, SeekOrigin.Begin);
				var size = ms.Length;
				var o = GridUtils.DeserializeBinary(ms, testObject.GetType());
				sp.Stop();
				var time = sp.ElapsedMilliseconds;

				var jsonconv = new Newtonsoft.Json.JsonSerializer();
				var writer = new StringWriter();
				jsonconv.Serialize(writer, testObject);
				var json1 = writer.ToString();
				writer = new StringWriter();
				jsonconv.Serialize(writer, o);
				var json2 = writer.ToString();
				Assert.IsTrue(json1.Equals(json2));
				return new long[] { time, size };
			}
		}


		[Test ()]
		public void Comm_TestGridUtilsCommListen()
		{
			var t = Task.Run(() => RunCommListener());
			Thread.Sleep(1000);
			RunCommWriter();
			Assert.IsTrue(t.Wait(1000));
		}

		public void RunCommWriter()
		{
			var l = new GridComm();
			l.SendMessage("TEST", "end");
		}

		void RunCommListener() {
			var l = new GridComm();
			var end = false;
			l.OnMessageReceived += (message) =>
			{
				end = "end".Equals(message);
				GridUtils.NotifyLog("TEST,INFO",message);
			};
			l.StartListener();
			while (!end)
			{
				Thread.Sleep(100);
			}
		}

		[Test ()]
		public void Comm_TestGridUtilsCommEvent()
		{
			var l = new GridComm();
			var resevent = new ManualResetEvent(false);
			l.OnUnknownEventReceived += (ev, data) =>
			{
				Assert.AreEqual("testev", ev);
				Assert.AreEqual("test", data["test"]);
				resevent.Set();
			};
			l.StartListener();
			l.Emit("TEST", "testev", new { test = "test" });
			resevent.WaitOne();
		}
	}
}

